package org.common.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.*;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.*;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * netty服务器
 */
@Slf4j
public class NettyServer {

    private final BaseNettyHandler handler;

    public NettyServer(BaseNettyHandler handler) {
        this.handler = handler;
    }

    public void start(int port, String protoType) {

        final EventLoopGroup boss = new NioEventLoopGroup(1);
        final EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.option(ChannelOption.SO_REUSEADDR, true);//允许重用端口
            bootstrap.option(ChannelOption.SO_BACKLOG, 512);//允许多少个新请求进入等待
            bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//是否使用内存池
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.childOption(ChannelOption.TCP_NODELAY, false);
            bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//是否使用内存池
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    switch (protoType) {
                        case "tcp" -> tcpPipelineConfig(pipeline);
                        case "websocket" -> webSocketPipelineConfig(pipeline);
                        default -> throw new RuntimeException("不支持的协议类型");
                    }
                    // ----------  自定义消息处理器 -----------
                    pipeline.addLast(handler);
                }
            });
            bootstrap.bind(port).sync();

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }));

        log.info("Start NettyServer ok! port={}", port);
    }

    /**
     * 基于tcp的pipeline配置
     */
    public static void tcpPipelineConfig(ChannelPipeline pipeline) {
        // ----------  解码器  -------------
        // 1. 读取数据的长度
        pipeline.addLast(new LengthFieldBasedFrameDecoder(10 * 1024 * 1024, 0, 4, 0, 4));
        // 2. 添加数据的长度到数据头
        pipeline.addLast(new LengthFieldPrepender(4));
        // 将ByteBuf转成byte[]
        pipeline.addLast(new ByteToMessageDecoder() {
            @Override
            protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
                if (in.isReadable()) {
                    byte[] bytes = new byte[in.readableBytes()];
                    in.readBytes(bytes);
                    out.add(bytes);
                }
            }
        });
        // 将打包好的数据由byte[]转成ByteBuf
        pipeline.addLast(new MessageToByteEncoder<byte[]>() {
            @Override
            protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception {
                out.writeBytes(msg);
            }
        });
    }

    /**
     * 基于websocket的pipeline配置
     */
    public static void webSocketPipelineConfig(ChannelPipeline pipeline) {
        // 1. 添加HTTP服务器编解码器，用于将数据转换成HTTP协议格式进行传输。
        pipeline.addLast(new HttpServerCodec());
        // 2. 添加HTTP对象聚合处理器，用于将HTTP请求或响应中的多个消息片段聚合成完整的消息。
        pipeline.addLast(new HttpObjectAggregator(65535));
        // 3.WebSocket协议处理器，用于处理握手、ping、pong等消息
        pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));
        // 将BinaryWebSocketFrame转成byte[]
        pipeline.addLast(new MessageToMessageDecoder<BinaryWebSocketFrame>() {
            @Override
            protected void decode(ChannelHandlerContext ctx, BinaryWebSocketFrame in, List<Object> out) throws Exception {
                ByteBuf content = in.content();
                byte[] bytes = new byte[content.readableBytes()];
                content.readBytes(bytes);
                out.add(bytes);
            }
        });
        // 将打包好的数据由byte[]转成BinaryWebSocketFrame
        pipeline.addLast(new MessageToMessageEncoder<byte[]>() {
            @Override
            protected void encode(ChannelHandlerContext ctx, byte[] msg, List<Object> out) throws Exception {
                ByteBuf buffer = ctx.alloc().buffer(msg.length);
                buffer.writeBytes(msg);
                out.add(new BinaryWebSocketFrame(buffer));
            }

        });
    }



}
